using System;
using System.Collections;
using System.Drawing;

namespace PDUConverter.SmartMessaging
{
    /// <summary>
    /// Represents an OTA (over-the-air) bitmap.
    /// </summary>
    public class OtaBitmap
    {
        private byte[] bitmap;
        private int dataLen;
        private int dataStart;
        private byte grayscales;
        private byte height;
        private byte infoField;
        private byte width;

        /// <summary>
        /// Creates a new OTA bitmap from an existing <see cref="T:System.Drawing.Bitmap" /> object.
        /// </summary>
        /// <param name="bitmap">The <see cref="T:System.Drawing.Bitmap" /> to create the OTA bitmap from.</param>
        public OtaBitmap(Bitmap bitmap) : this(BitmapToOtaBitmap(bitmap))
        {
        }

        /// <summary>
        /// Creates a new OTA bitmap from an existing byte array.
        /// </summary>
        /// <param name="otaBitmap">The byte array containing the OTA bitmap.</param>
        /// <exception cref="T:System.ArgumentNullException">otaBitmap is null.</exception>
        public OtaBitmap(byte[] otaBitmap)
        {
            if (otaBitmap == null)
            {
                throw new ArgumentException("otaBitmap");
            }
            int num = 0;
            this.infoField = otaBitmap[num++];
            this.width = otaBitmap[num++];
            this.height = otaBitmap[num++];
            this.grayscales = otaBitmap[num++];
            this.dataStart = num;
            this.dataLen = otaBitmap.Length - num;
            this.bitmap = new byte[otaBitmap.Length];
            otaBitmap.CopyTo(this.bitmap, 0);
        }

        /// <summary>
        /// Converts a <see cref="T:System.Drawing.Bitmap" /> into an OTA (over-the-air) bitmap.
        /// </summary>
        /// <param name="bitmap">The <see cref="T:System.Drawing.Bitmap" /> to convert. The maximum allowed
        /// size is 255x255 pixels, minimum is 1x1. The bitmap can be any
        /// pixel format, but only the black pixels are converted.
        /// Can be null to get an empty header.</param>
        /// <returns>The converted image. If bitmap is null, an empty OTA bitmap
        /// header and no data is returned.</returns>
        /// <exception cref="T:System.ArgumentException">bitmap is greater than 255x255 pixels.</exception>
        private static byte[] BitmapToOtaBitmap(Bitmap bitmap)
        {
            byte[] buffer = null;
            byte[] array = null;
            if (bitmap != null)
            {
                if (((bitmap.Height < 1) || (bitmap.Width < 1)) || ((bitmap.Height > 0xff) || (bitmap.Width > 0xff)))
                {
                    throw new ArgumentException("Invalid bitmap dimensions. Maximum size is 255x255, minimum size is 1x1 pixels.");
                }
                int num = 7;
                byte num2 = 0;
                ArrayList list = new ArrayList();
                for (int i = 0; i < bitmap.Height; i++)
                {
                    for (int j = 0; j < bitmap.Width; j++)
                    {
                        byte num5 = (byte) Math.Pow(2.0, (double) num);
                        if (bitmap.GetPixel(j, i).ToArgb() == Color.Black.ToArgb())
                        {
                            num2 = (byte) (num2 | num5);
                        }
                        if (num == 0)
                        {
                            list.Add(num2);
                            num2 = 0;
                            num = 7;
                        }
                        else
                        {
                            num--;
                        }
                    }
                }
                if (num < 7)
                {
                    list.Add(num2);
                }
                array = new byte[list.Count];
                list.CopyTo(array);
                byte[] buffer4 = new byte[4];
                buffer4[1] = (byte) bitmap.Width;
                buffer4[2] = (byte) bitmap.Height;
                buffer4[3] = 1;
                buffer = buffer4;
            }
            else
            {
                array = new byte[0];
                byte[] buffer5 = new byte[4];
                buffer5[3] = 1;
                buffer = buffer5;
            }
            byte[] buffer3 = new byte[buffer.Length + array.Length];
            buffer.CopyTo(buffer3, 0);
            array.CopyTo(buffer3, buffer.Length);
            return buffer3;
        }

        /// <summary>
        /// Converts the value of the specified instance into a <see cref="T:System.Drawing.Bitmap" />.
        /// </summary>
        public static explicit operator Bitmap(OtaBitmap b)
        {
            return b.ToBitmap();
        }

        /// <summary>
        /// Converts the specified value into a new instance of the <see cref="T:GsmComm.PduConverter.SmartMessaging.OtaBitmap" /> class.
        /// </summary>
        public static explicit operator OtaBitmap(byte[] b)
        {
            return new OtaBitmap(b);
        }

        /// <summary>
        /// Converts the specified value into a new instance of the <see cref="T:GsmComm.PduConverter.SmartMessaging.OtaBitmap" /> class.
        /// </summary>
        public static explicit operator OtaBitmap(Bitmap b)
        {
            return new OtaBitmap(b);
        }

        /// <summary>
        /// Converts the value of the specified instance into a byte array.
        /// </summary>
        public static implicit operator byte[](OtaBitmap b)
        {
            return b.ToByteArray();
        }

        /// <summary>
        /// Converts an OTA bitmap into a <see cref="T:System.Drawing.Bitmap" />.
        /// </summary>
        /// <param name="otaBitmap">The OTA bitmap to convert. Can be null.</param>
        /// <returns>The converted image. If otaBitmap is null, null is returned.
        /// null is also returned, if the height or width of the OTA bitmap is 0.</returns>
        /// <remarks>
        /// <para>The grayscales attribute of the bitmap is ignored, always a monochrome bitmap is created.</para>
        /// </remarks>
        private static Bitmap OtaBitmapToBitmap(byte[] otaBitmap)
        {
            if (otaBitmap == null)
            {
                return null;
            }
            int num = 0;
            byte num1 = otaBitmap[num++];
            byte width = otaBitmap[num++];
            byte height = otaBitmap[num++];
            byte num9 = otaBitmap[num++];
            if ((width == 0) || (height == 0))
            {
                return null;
            }
            Bitmap bitmap = new Bitmap(width, height);
            int num4 = 0;
            byte num5 = 0;
            for (int i = 0; i < height; i++)
            {
                for (int j = 0; j < width; j++)
                {
                    if (num4 == 0)
                    {
                        num5 = otaBitmap[num++];
                        num4 = 7;
                    }
                    else
                    {
                        num4--;
                    }
                    byte num8 = (byte) Math.Pow(2.0, (double) num4);
                    bitmap.SetPixel(j, i, ((num5 & num8) > 0) ? Color.Black : Color.White);
                }
            }
            return bitmap;
        }

        /// <summary>
        /// Returns the <see cref="T:System.Drawing.Bitmap" /> equivalent of this instance.
        /// </summary>
        /// <returns>The <see cref="T:System.Drawing.Bitmap" />.</returns>
        public Bitmap ToBitmap()
        {
            return OtaBitmapToBitmap(this.bitmap);
        }

        /// <summary>
        /// Returns the byte array equivalent of this instance.
        /// </summary>
        /// <returns>The byte array.</returns>
        public byte[] ToByteArray()
        {
            byte[] array = new byte[this.bitmap.Length];
            this.bitmap.CopyTo(array, 0);
            return array;
        }

        /// <summary>
        /// Gets the actual bitmap data.
        /// </summary>
        public byte[] Data
        {
            get
            {
                byte[] destinationArray = new byte[this.dataLen];
                Array.Copy(this.bitmap, this.dataStart, destinationArray, 0, this.dataLen);
                return destinationArray;
            }
        }

        /// <summary>
        /// Gets the bitmap's height.
        /// </summary>
        public byte Height
        {
            get
            {
                return this.height;
            }
        }

        /// <summary>
        /// Gets the bitmap's InfoField.
        /// </summary>
        public byte InfoField
        {
            get
            {
                return this.infoField;
            }
        }

        /// <summary>
        /// Gets the bitmap's number of grayscales.
        /// </summary>
        public byte NumGrayscales
        {
            get
            {
                return this.grayscales;
            }
        }

        /// <summary>
        /// Gets the bitmap's width.
        /// </summary>
        public byte Width
        {
            get
            {
                return this.width;
            }
        }
    }
}